package com.chengyi.serialport.demo.activity

import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.databinding.ObservableField
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.chengyi.serialboard.sdk.Openable
import com.chengyi.serialport.demo.MyApplication
import com.chengyi.serialport.demo.R
import com.chengyi.serialport.demo.controller.AppController
import com.chengyi.serialport.demo.utils.SpannableStringUtils
import com.hjq.toast.Toaster
import com.wyj.databinding.BaseViewModel
import com.wyj.utils.isNullOrEmpty
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import me.goldze.mvvmhabit.binding.command.BindingAction
import me.goldze.mvvmhabit.binding.command.BindingCommand
import java.io.Closeable
import java.text.SimpleDateFormat
import java.util.*

abstract class BaseSerialPortViewModel<T> : BaseViewModel(), Closeable where T : Openable, T : Closeable {
    val hexText = ObservableField<String>()
    val serialPortUtil = ObservableField<T>()
    abstract val serialportPath: ObservableField<String>
    abstract val baudrate: ObservableField<String>
    abstract fun createSerialPortUtil(path: String, baudrate: Int): T?
    abstract val textHex: TextView?
    private val onCloseClick = BindingAction {
        serialPortUtil.get()?.close()
        serialPortUtil.set(null)
        onSerialPortClosed()
    }
    val onOpenCloseClick = BindingAction {
        if (serialPortUtil.get() == null) {
            val path = serialportPath.get()
            val baudrate = baudrate.get()?.toIntOrNull()
            if (isNullOrEmpty(path)) {
                Toaster.show("请选择串口号")
                return@BindingAction
            }
            if (baudrate == null) {
                Toaster.show("请选择波特率")
                return@BindingAction
            }
            createSerialPortUtil(path, baudrate)?.also {
                if (it.open()) {
                    serialPortUtil.set(it)
                }
            }
        } else {
            onCloseClick.call()
        }
    }
    val onSerialportPathClick = BindingAction {
        AppController.getOnSerialPortPathClick(uiChangeLiveData.getContext() ?: return@BindingAction, BindingCommand { tag ->
            serialportPath.set(tag)
            onCloseClick.call()
        }).call()
    }
    val onBaudrateClick = BindingAction {
        AppController.getOnBaudrateClick(uiChangeLiveData.getContext() ?: return@BindingAction, BindingCommand { tag ->
            baudrate.set(tag)
            onCloseClick.call()
        }).call()
    }
    open val onClearClick = BindingAction {
        textHex?.let {
            it.text = ""
            it.scrollTo(0, 0)
        }
    }

    protected open fun onSerialPortClosed() {}
    protected fun closeJob(liveData: MutableLiveData<Job?>): Boolean {
        val job = liveData.value
        if (job != null) {
            job.cancel()
            liveData.value = null
            return true
        }
        return false
    }

    protected fun onSerialPortAnalysisCallback(str: String) {
        val time = SimpleDateFormat("HH:mm:ss.SSS").format(Date())
        val sb = SpannableStringUtils.Builder()
        sb.append("${time}[${serialportPath.get()}][解析]").setForegroundColor(ContextCompat.getColor(MyApplication.application, R.color.qmui_config_color_green))
        sb.append(str)
        appendHexText(sb.create())
    }

    protected fun onSerialPortSendCallback(hex: String) {
        val str = hex.replace("[0-9A-Fa-f]{1,2}".toRegex(), " $0")
        val time = SimpleDateFormat("HH:mm:ss.SSS").format(Date())
        val sb = SpannableStringUtils.Builder()
        sb.append("${time}[${serialportPath.get()}][Send]").setForegroundColor(ContextCompat.getColor(MyApplication.application, R.color.qmui_config_color_blue))
        sb.append(str)
        appendHexText(sb.create())
    }

    protected fun onSerialPortReceiveCallback(hex: String) {
        val str = hex.replace("[0-9A-Fa-f]{1,2}".toRegex(), " $0")
        val time = SimpleDateFormat("HH:mm:ss.SSS").format(Date())
        val sb = SpannableStringUtils.Builder()
        sb.append("${time}[${serialportPath.get()}][Read]").setForegroundColor(ContextCompat.getColor(MyApplication.application, R.color.qmui_config_color_orange))
        sb.append(str)
        appendHexText(sb.create())
    }

    protected fun runSerialPortCommand(block: (T) -> Unit) {
        serialPortUtil.get()?.let(block) ?: Toaster.show("请先打开串口")
    }

    private fun appendHexText(text: CharSequence) {
        viewModelScope.launch {
            textHex?.let { textHex ->
                textHex.append(text)
                textHex.append("\r\n")
                val offset = textHex.lineCount * textHex.lineHeight
                val y = offset - textHex.height + textHex.lineHeight
                if (y > 0) {
                    textHex.scrollTo(0, y)
                }
            }
        }
    }

    override fun close() {
        onCloseClick.call()
    }
}